//
//  AvoidCrash.m
//  LzlPictureBrower
//
//  Created by luozhilin on 2018/6/5.
//  Copyright © 2018年 luozhilin. All rights reserved.
//

#import "YYAvoidCrash.h"
#import "NSObject+AviodCrash.h"
#import "NSArray+AviodCrash.h"
#import "NSMutableArray+AviodCrash.h"
#import "NSDictionary+AviodCrash.h"
#import "NSMutableDictionary+AvoidCrash.h"
#import "NSString+AviodCrash.h"

@implementation YYAvoidCrash

+ (void)becomeEffective
{
    [self effectiveIfDealWithNoneSel:NO];
}

+ (void)makeAllEffective
{
    [self effectiveIfDealWithNoneSel:YES];
}


+ (void)effectiveIfDealWithNoneSel:(BOOL)dealWithNoneSel
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [NSObject avoidCrashExchangeMethodIfDealWithNoneSel:dealWithNoneSel];
        
        [NSArray avoidCrashExchangeMethod];
        [NSMutableArray avoidCrashExchangeMethod];
        [NSDictionary avoidCrashExchangeMethod];
        [NSMutableDictionary avoidCrashExchangeMethod];
        [NSString avoidCrashExchangeMethod];
    });
}

+ (void)setupNoSelClassAvoidCrashWithClassNameArray:(NSArray<NSString *> *)classArray
{
    [NSObject setupNoSelClassAvoidCrashWithClassNameArray:classArray];
}

+ (void)setupNoSelClassAvoidCrashWithPrefixClassNameArray:(NSArray<NSString *> *)prefixClassArray
{
    [NSObject setupNoSelClassAvoidCrashWithPrefixClassNameArray:prefixClassArray];
}

/**替换类的方法
 *originalClass：要替换类的名称
 *originalSel：要替换的方法
 *newMethod：自定义的方法
 */
+ (void)exchangeClassMethod:(Class)originalClass originalMethod:(SEL)originalSel newMethod:(SEL)newMethod
{
    Method originalMethod = class_getClassMethod(originalClass, originalSel);
    Method swizzledMethod = class_getClassMethod(originalClass, newMethod);
    
    method_exchangeImplementations(originalMethod, swizzledMethod);
}

/**替换实例对象的方法
 *originalClass：要替换类的名称
 *originalSel：要替换的方法
 *newMethod：自定义的方法
 */
+ (void)exchangeInstanceMethod:(Class)originalClass originalMethod:(SEL)originalSel newMethod:(SEL)newMethod
{
    Method originalMethod = class_getInstanceMethod(originalClass, originalSel);
    Method swizzledMethod = class_getInstanceMethod(originalClass, newMethod);
    
    BOOL didAddMethod = class_addMethod(originalClass, originalSel,
                                        method_getImplementation(swizzledMethod),
                                        method_getTypeEncoding(swizzledMethod));
    
    if (didAddMethod) {
        class_replaceMethod(originalClass, newMethod,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    }else{
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

/**
 *  获取堆栈主要崩溃精简化的信息<根据正则表达式匹配出来>
 *
 *  @param callStackSymbols 堆栈主要崩溃信息
 *
 *  @return 堆栈主要崩溃精简化的信息
 */
+ (NSString *)getMainCallStackSymbolMessageWithCallStackSymbols:(NSArray <NSString *> *)callStackSymbols
{
     //mainCallStackSymbolMsg的格式为   +[类名 方法名]  或者 -[类名 方法名]
    __block NSString *mainCallStackSymbolMsg = nil;
    
    NSString *regularString = @"[-\\+]\\[.+\\]";
    
    NSError *error ;
    NSRegularExpression *regularExp = [[NSRegularExpression alloc] initWithPattern:regularString options:NSRegularExpressionCaseInsensitive error:&error];
    if (error) {
        NSLog(@"NSRegularExpressionError = %@",error.description);
    }
    
    // 遍历堆栈信息，前两个是自定义的方法调用
    for (int i=2; i<callStackSymbols.count; i++) {
        NSString *callStackSymbol = callStackSymbols[i];
        
        [regularExp enumerateMatchesInString:callStackSymbol options:NSMatchingReportProgress range:NSMakeRange(0, callStackSymbol.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
            if (result) {
                NSString* tempCallStackSymbolMsg = [callStackSymbol substringWithRange:result.range];
                
                //get className
                NSString *className = [tempCallStackSymbolMsg componentsSeparatedByString:@" "].firstObject;
                className = [className componentsSeparatedByString:@"["].lastObject;
                
                NSBundle *bundle = [NSBundle bundleForClass:NSClassFromString(className)];
                
                //filter category and system class
                if (![className hasSuffix:@")"] && bundle == [NSBundle mainBundle]) {
                    mainCallStackSymbolMsg = tempCallStackSymbolMsg;
                    
                }
                *stop = YES;
            }
        }];
        if (mainCallStackSymbolMsg.length) {
            break;
        }
    }
    return mainCallStackSymbolMsg;
}

/**
 *  提示崩溃的信息(控制台输出)
 *
 *  @param exception   捕获到的异常
 *  @param defaultToDo 这个框架里默认的做法
 */
+ (void)errorMessageWithException:(NSException *)exception defaultToDo:(NSString *)defaultToDo
{
    // 获取崩溃的调用堆栈
    NSArray *callStackSymbols = [NSThread callStackSymbols];
    
    NSString *mainCallStackSymbolMsg = [self getMainCallStackSymbolMessageWithCallStackSymbols:callStackSymbols];
    
    if (mainCallStackSymbolMsg == nil) {
        
        mainCallStackSymbolMsg = @"崩溃方法定位失败,请您查看函数调用栈来排查错误原因";
        
    }
    
    NSString *errorName = exception.name;
    NSString *errorReason = exception.reason;
    //errorReason 可能为 -[__NSCFConstantString avoidCrashCharacterAtIndex:]: Range or index out of bounds
    //将avoidCrash去掉
    errorReason = [errorReason stringByReplacingOccurrencesOfString:@"avoidCrash" withString:@""];
    
    NSString *errorPlace = [NSString stringWithFormat:@"Error Place:%@",mainCallStackSymbolMsg];
    
    NSString *logErrorMessage = [NSString stringWithFormat:@"\n\n%@\n\n%@\n%@\n%@\n%@",AvoidCrashSeparatorWithFlag, errorName, errorReason, errorPlace, defaultToDo];
    
    logErrorMessage = [NSString stringWithFormat:@"%@\n\n%@\n\n",logErrorMessage,AvoidCrashSeparator];
    YYAvoidCrashLog(@"%@",logErrorMessage);
}
@end
